<!DOCTYPE html>
<html>
<head>
    <!-- <title>小智聊天室</title> -->
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
    <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
    <link href="/static/styles.css" rel="stylesheet">
</head>
<body class="bg-gray-100">
    <div class="chat-container">
        <div class="header">
            <!-- <h1 class="text-2xl font-bold">小智聊天室</h1> -->
            <button class="settings-btn" onclick="toggleSettings()">
                <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
                </svg>
            </button>
        </div>

        <div class="p-4 bg-gray-50 border-b">
            <div class="flex items-center justify-between">
                <div class="flex items-center space-x-4">
                    <span id="connectionStatus" class="status disconnected">未连接</span>
                    <span class="text-sm text-gray-600">设备ID: {{ device_id }}</span>
                </div>
            </div>
        </div>

        <div class="chat-box" id="chatBox"></div>

        <div class="input-container">
            <input type="text" id="messageInput" placeholder="输入消息..." onkeypress="handleKeyPress(event)">
            <button class="btn-primary" onclick="sendMessage()">
                <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
                    <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-8.707l-3-3a1 1 0 00-1.414 1.414L10.586 9H7a1 1 0 100 2h3.586l-1.293 1.293a1 1 0 101.414 1.414l3-3a1 1 0 000-1.414z" clip-rule="evenodd" />
                </svg>
                <span>发送</span>
            </button>
            <button id="recordButton" class="btn-success">
                <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
                    <path d="M2 3a1 1 0 011-1h2.153a1 1 0 01.986.836l.74 4.435a1 1 0 01-.54 1.06l-1.548.773a11.037 11.037 0 006.105 6.105l.774-1.548a1 1 0 011.059-.54l4.435.74a1 1 0 01.836.986V17a1 1 0 01-1 1h-2C7.82 18 2 12.18 2 5V3z" />
                </svg>
            </button>
        </div>
    </div>

    <div class="settings-panel" id="settingsPanel">
        <h2 class="text-xl font-bold mb-4">设置</h2>
        <div class="space-y-4">
            <div>
                <label class="block text-sm font-medium text-gray-700">远程服务器地址</label>
                <input type="text" id="remoteServerUrl" class="w-full" value="{{ ws_url }}" placeholder="例如: ws://your-server:9005">
                <p class="text-sm text-gray-500 mt-1">代理服务器连接的远程地址</p>
            </div>
            <div>
                <label class="block text-sm font-medium text-gray-700">本地代理地址</label>
                <input type="text" id="localProxyUrl" class="w-full" value="{{ local_proxy_url }}" placeholder="例如: ws://localhost:5002">
                <p class="text-sm text-gray-500 mt-1">前端连接本地代理地址</p>
            </div>
            <div class="space-y-2">
                <div class="flex items-center justify-between">
                    <label class="block text-sm font-medium text-gray-700">Token 设置</label>
                    <div class="flex items-center">
                        <label class="toggle-switch">
                            <input type="checkbox" id="enableToken" {% if enable_token %}checked{% endif %}>
                            <span class="toggle-slider"></span>
                        </label>
                        <span class="ml-2 text-sm font-medium text-gray-700" id="tokenStatus">
                                {{#if enable_token}}
                                已开启
                                {{else}}
                                已关闭
                                {{/if}}
                            </span>
                    </div>
                </div>
                <input type="text" id="serverToken" class="w-full transition-all duration-200" 
                    value="{{ token }}" {% if not enable_token %}disabled{% endif %}
                    style="{% if not enable_token %}background-color: #f3f4f6; border-color: #e5e7eb;{% endif %}">
                <p class="text-sm text-gray-500">开启后将在连接时携带 Token</p>
            </div>
            <div class="flex justify-end space-x-2 mt-4">
                <button class="btn-primary" onclick="saveSettings()">保存配置</button>
                <button class="btn-danger" onclick="toggleSettings()">取消</button>
            </div>
        </div>
    </div>

    <div class="overlay" id="overlay" onclick="toggleSettings()"></div>

    <div class="voice-call-mode" id="voiceCallMode">
        <div class="voice-header">
            <div class="voice-title">语音通话</div>
            <div class="voice-status-text">正在通话中...</div>
        </div>

        <div class="voice-main">
            <div class="voice-avatar-container">
                <div class="voice-avatar">
                    <div class="ripple-1"></div>
                    <div class="ripple-2"></div>
                    <div class="ripple-3"></div>
                    <img src="../static/images/avatar.jpg" alt="AI Assistant">
                </div>
            </div>

            <div class="voice-wave-container">
                <div class="voice-wave">
                    <div class="wave-line"></div>
                    <div class="wave-line"></div>
                    <div class="wave-line"></div>
                    <div class="wave-line"></div>
                    <div class="wave-line"></div>
                    <div class="wave-line"></div>
                    <div class="wave-line"></div>
                    <div class="wave-line"></div>
                    <div class="wave-line"></div>
                    <div class="wave-line"></div>
                </div>
            </div>

            <div class="voice-info">
                <div class="voice-name">小智</div>
            </div>
        </div>

        <div class="voice-controls">
            <button class="voice-btn mic" id="micButton">
                <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                    <path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"></path>
                    <path d="M19 10v2a7 7 0 0 1-14 0v-2"></path>
                    <line x1="12" y1="19" x2="12" y2="23"></line>
                    <line x1="8" y1="23" x2="16" y2="23"></line>
                </svg>
            </button>
            <button class="voice-btn hangup" onclick="stopRecording()">
                <svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24">
                    <path d="M151.04 658.944c44.544 0.512 149.504-19.456 190.464-60.416 9.728-9.728 15.872-19.968 17.92-31.232 7.168-32.768 13.312-58.88000001 20.992-66.56 4.096-4.096 12.288-5.12 31.232-6.144l3.072 0c51.71199999-3.584 63.488-3.072 85.504-3.07199999l16.384-1e-8c18.944 0.512 32.768 0.512 83.96800001 3.584l2.04799999 0c24.576 1.536 40.44800001 2.56 47.104 8.192 6.656 5.632 11.264 20.48 15.872 52.224 5.632 38.4 32.256 66.048 78.848 82.432 36.864 12.8 79.872 17.408 118.272 20.992l3.072 0.512c32.768 3.072 58.88-4.608 78.336-24.06400001 21.504-21.504 27.648-50.176 30.72-65.53599999 0.512-1.024 0.512-2.56 0.512-3.584 7.68-34.304 4.608-67.584-8.19200001-92.16-19.456-36.35199999-49.664-78.336-166.39999999-112.64-100.864-29.696-176.64-40.96-280.576-42.496-97.792-1.024-135.168 4.09600001-217.6 17.408-92.16 14.848-165.376 44.03199999-201.216 79.872C68.096 450.56 41.984 476.672 49.664 559.104c8.192 86.528 53.248 99.328 101.376 99.84z" fill="currentColor"></path>
                </svg>
            </button>
        </div>
    </div>

    <script>
        let ws = null;
        let isConnected = false;
        let mediaRecorder = null;
        let audioChunks = [];
        let isRecording = false;
        let sessionId = null;
        let audioQueue = [];
        let isPlaying = false;
        let audioContext = null;
        let nextPlayTime = 0;
        let audioProcessor = null;
        let audioStream = null;
        let processorNode = null;
        const wsUrl = "{{ local_proxy_url }}";  // 使用配置的本地代理地址
        let silenceTimer = null;
        const SILENCE_TIMEOUT = 1000; // 1秒没有声音就自动停止
        let lastAudioTime = 0;
        let currentResponseDiv = null;  // 当前回复的消息 div
        const BUFFER_THRESHOLD = 3;     // 缓冲区阈值，当积累了这么多音频片段后开始播放
        let audioBufferQueue = [];      // 音频缓冲区队列
        let isAudioContextInitialized = false;
        let lastUserAudioLevel = 0;
        let lastAIAudioLevel = 0;
        let animationCheckInterval = null;
        let isPlaybackScheduled = false; // 新增变量：标记是否已安排音频播放

        // 初始化音频处理器
        async function initAudioProcessor() {
            try {
                if (!audioContext) {
                    // 尝试创建音频上下文，但不强制指定采样率以提高兼容性
                    try {
                        // 首先尝试指定采样率
                        audioContext = new (window.AudioContext || window.webkitAudioContext)({
                            sampleRate: 16000
                        });
                        console.log('成功创建指定采样率的AudioContext:', audioContext.sampleRate);
                    } catch (sampleRateError) {
                        console.warn('指定采样率失败，使用浏览器默认采样率:', sampleRateError);
                        // 不指定采样率，使用浏览器默认值
                        audioContext = new (window.AudioContext || window.webkitAudioContext)();
                        console.log('使用默认采样率的AudioContext:', audioContext.sampleRate);
                    }
                    
                    // 检查是否在HTTPS环境或localhost
                    const isSecureOrigin = location.protocol === 'https:' || 
                                          location.hostname === 'localhost' || 
                                          location.hostname === '127.0.0.1' || 
                                          location.hostname.match(/^10\.\d+\.\d+\.\d+$/) || 
                                          location.hostname.match(/^192\.168\.\d+\.\d+$/);
                    
                    if (!isSecureOrigin) {
                        console.warn('警告: 您正在非安全上下文(非HTTPS/非localhost)中运行，某些浏览器可能限制麦克风访问');
                    }
                    
                    // 尝试使用AudioWorklet或者降级到ScriptProcessorNode
                    try {
                        // 先验证文件是否可以访问
                        console.log('尝试加载AudioWorklet模块: /static/audio-processor.js');
                        
                        // 尝试加载AudioWorklet模块
                        try {
                            await audioContext.audioWorklet.addModule('/static/audio-processor.js');
                            console.log('成功加载AudioWorklet模块');
                        } catch (workletError) {
                            console.error('加载AudioWorklet模块失败，尝试使用ScriptProcessorNode替代:', workletError);
                            
                            // 创建替代的ScriptProcessorNode
                            console.log('创建ScriptProcessorNode作为替代方案');
                            audioContext.scriptProcessorNode = audioContext.createScriptProcessor(4096, 1, 1);
                            
                            // 设置处理器函数
                            audioContext.scriptProcessorNode.onaudioprocess = function(e) {
                                const inputBuffer = e.inputBuffer;
                                const inputData = inputBuffer.getChannelData(0);
                                
                                // 复制数据到Float32Array
                                const audioData = new Float32Array(inputData);
                                
                                // 发送音频数据到主线程处理函数
                                if (audioContext.audioDataCallback) {
                                    audioContext.audioDataCallback(audioData);
                                }
                            };
                            
                            console.log('ScriptProcessorNode替代方案创建成功');
                            // 不抛出错误，继续执行
                        }
                    } catch (error) {
                        console.error('音频处理初始化完全失败:', error);
                        throw new Error('音频处理器初始化失败，请检查浏览器兼容性或刷新页面重试');
                    }
                }
                return audioContext;
            } catch (error) {
                console.error('初始化音频上下文失败:', error);
                throw error;
            }
        }

        // 修改为点击切换通话状态
        function initRecordButton() {
            const button = document.getElementById('recordButton');
            
            // 点击切换通话状态
            button.addEventListener('click', async () => {
                if (!isRecording) {
                    await startRecording();
                    button.style.background = '#ea4335';
                } else {
                    stopRecording();
                    button.style.background = '#34a853';
                }
            });
        }

        // 开始录音
        async function startRecording() {
            console.log('===== 开始录音流程 =====');
            try {
                // 显示通话界面
                console.log('步骤1: 显示通话界面');
                const voiceCallMode = document.getElementById('voiceCallMode');
                voiceCallMode.classList.add('active');

                // 先停止所有正在播放的音频
                console.log('步骤2: 停止所有正在播放的音频');
                if (audioContext) {
                    console.log('  - 关闭现有的AudioContext');
                    await audioContext.close();
                    audioContext = null;
                    audioQueue = [];
                    isPlaying = false;
                    nextPlayTime = 0;
                }
                
                console.log('步骤3: 初始化音频处理器(initAudioProcessor)');
                await initAudioProcessor();
                console.log('  - 音频处理器初始化成功');
                
                // 如果已经有流，先停止它
                console.log('步骤4: 准备获取麦克风流');
                if (audioStream) {
                    console.log('  - 停止现有的音频流');
                    audioStream.getTracks().forEach(track => track.stop());
                }
                
                // 添加错误提示
                try {
                    // 先检查媒体设备访问权限状态
                    const permissionStatus = await navigator.permissions.query({ name: 'microphone' });
                    console.log('麦克风权限状态:', permissionStatus.state);

                    // 为不同浏览器和设备提供兼容性选项
                    const baseConstraints = {
                        echoCancellation: true,
                        noiseSuppression: true,
                        autoGainControl: true,
                        channelCount: 1
                    };

                    // 尝试获取用户媒体设备（麦克风）
                    try {
                        // 首先尝试指定采样率
                        audioStream = await navigator.mediaDevices.getUserMedia({
                            audio: {
                                ...baseConstraints,
                                sampleRate: 16000
                            }
                        });
                        console.log('成功获取指定采样率的麦克风访问权限');
                    } catch (sampleRateError) {
                        console.warn('指定采样率失败，使用浏览器默认采样率:', sampleRateError);
                        // 不指定采样率，使用浏览器默认值
                        audioStream = await navigator.mediaDevices.getUserMedia({
                            audio: baseConstraints
                        });
                        console.log('成功获取默认采样率的麦克风访问权限');
                    }
                } catch (err) {
                    console.error('获取麦克风权限失败:', err);
                    
                    // 针对不同类型的错误提供更具体的提示
                    let errorMsg = '获取麦克风权限失败，请检查浏览器设置';
                    if (err.name === 'NotAllowedError') {
                        errorMsg = '请在浏览器中允许麦克风访问权限\n' +
                                  'Chrome浏览器: 点击地址栏左侧的锁图标 > 权限 > 麦克风 > 允许\n' +
                                  'Firefox浏览器: 点击地址栏左侧的信息图标 > 连接 > 更多信息 > 权限 > 麦克风 > 使用默认';
                    } else if (err.name === 'NotFoundError') {
                        errorMsg = '未检测到麦克风设备，请检查设备连接';
                    } else if (err.name === 'NotReadableError') {
                        errorMsg = '麦克风正在被其他应用使用，请先关闭其他使用麦克风的程序';
                    } else if (err.name === 'SecurityError') {
                        errorMsg = '安全错误：请确保在HTTPS环境或localhost中使用此功能\n' +
                                  '当前环境: ' + location.protocol + '//' + location.hostname;
                    } else if (err.name === 'OverconstrainedError') {
                        errorMsg = '浏览器不支持某些音频参数，请刷新页面重试';
                    }
                    
                    alert(errorMsg);
                    isRecording = false;
                    updateRecordButtonStyle(false);
                    throw err;
                }
                
                // 创建音频源
                console.log('步骤8: 创建音频源');
                const source = audioContext.createMediaStreamSource(audioStream);
                
                // 如果已经有处理节点，先断开连接并重置
                if (processorNode) {
                    processorNode.port.postMessage({ type: 'reset' });
                    processorNode.disconnect();
                }
                
                // 创建处理器节点
                console.log('步骤9: 创建音频处理器节点');
                if (audioContext.scriptProcessorNode) {
                    console.log('  - 使用ScriptProcessorNode替代方案');
                    processorNode = audioContext.scriptProcessorNode;
                    
                    // 设置音频数据回调函数
                    audioContext.audioDataCallback = function(audioData) {
                        if (isRecording && ws && ws.readyState === WebSocket.OPEN) {
                            const audioLevel = detectAudioLevel(audioData);
                            updateUserWaveAnimation(audioLevel);
                            ws.send(audioData.buffer);
                            lastAudioTime = Date.now();
                        }
                    };
                } else {
                    console.log('  - 使用AudioWorkletProcessor');
                    try {
                        processorNode = new AudioWorkletNode(audioContext, 'audio-processor', {
                            processorOptions: {
                                bufferSize: 960 // 60ms at 16kHz
                            }
                        });
                        
                        // 处理音频数据
                        processorNode.port.onmessage = (e) => {
                            if (isRecording && ws && ws.readyState === WebSocket.OPEN) {
                                if (e.data instanceof Float32Array) {
                                    const audioLevel = detectAudioLevel(e.data);
                                    updateUserWaveAnimation(audioLevel);
                                    ws.send(e.data.buffer);
                                    lastAudioTime = Date.now();
                                } else if (e.data.buffer) {
                                    const audioData = new Float32Array(e.data.buffer);
                                    const audioLevel = detectAudioLevel(audioData);
                                    updateUserWaveAnimation(audioLevel);
                                    ws.send(e.data.buffer);
                                    lastAudioTime = Date.now();
                                }
                            }
                        };
                    } catch (nodeError) {
                        console.error('创建AudioWorkletNode失败，尝试使用ScriptProcessorNode:', nodeError);
                        
                        // 再次尝试创建ScriptProcessorNode作为后备
                        audioContext.scriptProcessorNode = audioContext.createScriptProcessor(4096, 1, 1);
                        audioContext.scriptProcessorNode.onaudioprocess = function(e) {
                            const inputBuffer = e.inputBuffer;
                            const inputData = inputBuffer.getChannelData(0);
                            const audioData = new Float32Array(inputData);
                            if (audioContext.audioDataCallback) {
                                audioContext.audioDataCallback(audioData);
                            }
                        };
                        
                        processorNode = audioContext.scriptProcessorNode;
                        audioContext.audioDataCallback = function(audioData) {
                            if (isRecording && ws && ws.readyState === WebSocket.OPEN) {
                                const audioLevel = detectAudioLevel(audioData);
                                updateUserWaveAnimation(audioLevel);
                                ws.send(audioData.buffer);
                                lastAudioTime = Date.now();
                            }
                        };
                    }
                }
                
                // 连接音频节点
                console.log('步骤10: 连接音频节点');
                source.connect(processorNode);
                processorNode.connect(audioContext.destination);
                
                console.log('步骤11: 开始录音流程完成');
                
                // 发送开始录音消息
                if (ws && ws.readyState === WebSocket.OPEN) {
                    const startMessage = JSON.stringify({
                        type: "listen",
                        state: "start",
                        mode: "auto"
                    });
                    ws.send(startMessage);
                }

                isRecording = true;
                lastAudioTime = Date.now();
                
                // 启动静音检测
                silenceTimer = setInterval(() => {
                    if (isRecording && Date.now() - lastAudioTime > SILENCE_TIMEOUT) {
                        // 检测到静音，发送静音帧
                        if (ws && ws.readyState === WebSocket.OPEN) {
                            const silenceFrame = new Int16Array(960);
                            ws.send(silenceFrame.buffer);
                        }
                    }
                }, 100);

                updateRecordButtonStyle(true);

            } catch (err) {
                console.error('Error starting recording:', err);
                updateRecordButtonStyle(false);
                alert('无法访问麦克风');
            }
        }

        // 停止录音
        function stopRecording() {
            // 隐藏通话界面
            const voiceCallMode = document.getElementById('voiceCallMode');
            voiceCallMode.classList.remove('active');

            if (silenceTimer) {
                clearInterval(silenceTimer);
                silenceTimer = null;
            }
            
            // 发送停止消息
            if (ws && ws.readyState === WebSocket.OPEN) {
                const stopMessage = JSON.stringify({
                    type: "listen",
                    state: "stop",
                    mode: "auto"
                });
                ws.send(stopMessage);
            }

            // 清理资源
            if (audioStream) {
                audioStream.getTracks().forEach(track => track.stop());
                audioStream = null;
            }
            
            if (processorNode) {
                processorNode.port.postMessage({ type: 'reset' });
                processorNode.disconnect();
                processorNode = null;
            }
            
            // 等待当前音频播放完成
            if (audioContext) {
                const currentTime = audioContext.currentTime;
                if (nextPlayTime > currentTime) {
                    setTimeout(() => {
                        if (audioContext) {
                            audioContext.close();
                            audioContext = null;
                        }
                        audioQueue = [];
                        isPlaying = false;
                        isPlaybackScheduled = false;
                        nextPlayTime = 0;
                    }, (nextPlayTime - currentTime) * 1000);
                } else {
                    audioContext.close();
                    audioContext = null;
                    audioQueue = [];
                    isPlaying = false;
                    isPlaybackScheduled = false;
                    nextPlayTime = 0;
                }
            }
            
            isRecording = false;
            updateRecordButtonStyle(false);
            showUserWaveAnimation(false);
            showAIWaveAnimation(false);

            if (animationCheckInterval) {
                clearInterval(animationCheckInterval);
                animationCheckInterval = null;
            }
            
            updateUserWaveAnimation(0);
            updateAIWaveAnimation(0);
        }

        // 连接WebSocket
        function connect() {
        if (ws && ws.readyState === WebSocket.OPEN) {
            console.log('WebSocket已连接，无需重复连接');
            return;
        }
        
        const wsUrl = document.getElementById('proxyUrl')?.value || 'ws://localhost:8893';
        console.log('尝试连接WebSocket:', wsUrl);
        ws = new WebSocket(wsUrl);
        
        ws.onopen = function() {
            console.log('WebSocket连接已建立！');
            isConnected = true;
            document.getElementById('connectionStatus').className = 'status connected';
            document.getElementById('connectionStatus').textContent = '已连接';
            
            // 发送hello消息
            const helloMessage = {
                type: "hello",
                version: 3,
                transport: "websocket",
                audio_params: {
                    format: "opus",
                    sample_rate: 16000,
                    channels: 1,
                    frame_duration: 60
                }
            };
            console.log('准备发送hello消息:', helloMessage);
            ws.send(JSON.stringify(helloMessage));
            console.log('hello消息已发送');
        };
        
        ws.onclose = function(event) {
            console.log('WebSocket closed with code:', event.code, 'reason:', event.reason);
            isConnected = false;
            sessionId = null;
            document.getElementById('connectionStatus').className = 'status disconnected';
            document.getElementById('connectionStatus').textContent = '未连接';
            
            // 3秒后重连
            setTimeout(connect, 3000);
        };
        
        ws.onerror = function(error) {
            console.error('WebSocket错误:', error);
            document.getElementById('connectionStatus').className = 'status error';
            document.getElementById('connectionStatus').textContent = '错误';
        };
        
        // 记录上一次处理的音频消息时间戳，用于防止重复处理
        let lastAudioMessageTimestamp = 0;
        
        ws.onmessage = async function(event) {
            try {
                console.log('收到WebSocket消息:', event.data instanceof Blob ? '音频数据' : event.data);
                if (event.data instanceof Blob) {
                    showAIWaveAnimation(true);
                    
                    // 为防止重复处理相同的音频数据，添加一个时间戳检查
                    const currentTime = Date.now();
                    if (currentTime - lastAudioMessageTimestamp > 50) { // 允许50ms的间隔
                        lastAudioMessageTimestamp = currentTime;
                        queueAudio(event.data);
                    } else {
                        console.warn('Skipping potential duplicate audio message');
                    }
                } else {
                    // 处理文本消息
                    const data = JSON.parse(event.data);
                    console.log('解析到文本消息:', data);
                    
                    if (data.type === 'hello') {
                        console.log('收到hello响应！');
                        sessionId = data.session_id;
                        console.log('Session ID:', sessionId);
                    } else if (data.type === 'stt') {
                        // 只有在录音状态且没有source字段时才显示语音识别结果
                        if (data.text && data.text.trim() && isRecording && !data.source) {
                            appendMessage('user', data.text);  // 直接显示文本，不加[语音识别]前缀
                        }
                    } else if (data.type === 'tts') {
                        if (data.state === 'start') {
                            showAIWaveAnimation(true);
                            // 创建新的回复消息
                            if (data.text && data.text.trim()) {
                                currentResponseDiv = appendMessage('server', data.text);
                            }
                        } else if (data.state === 'sentence_start') {
                            showAIWaveAnimation(true);
                            // 如果是新的句子，且有文本内容
                            if (data.text && data.text.trim()) {
                            if (!currentResponseDiv) {
                                    // 如果没有当前回复div，创建一个新的
                                currentResponseDiv = appendMessage('server', data.text);
                            } else {
                                    // 如果已有回复div，追加内容
                                    currentResponseDiv.innerHTML += ' ' + data.text;
                                    // 滚动到底部
                                const chatBox = document.getElementById('chatBox');
                                chatBox.scrollTop = chatBox.scrollHeight;
                                }
                            }
                        } else if (data.state === 'end' || data.state === 'stop') {
                            showAIWaveAnimation(false);
                            // 结束当前回复
                            currentResponseDiv = null;
                        }
                    }
                }
            } catch (e) {
                console.error('Error processing message:', e);
            }
        };
        }
        
        // 添加消息到聊天框
        function appendMessage(type, text) {
            const chatBox = document.getElementById('chatBox');
            const messageDiv = document.createElement('div');
            messageDiv.className = `message ${type}`;
            
            const contentDiv = document.createElement('div');
            contentDiv.className = 'message-content';
            
            // 处理消息内容，支持换行
            const formattedMessage = text.replace(/\n/g, '<br>');
            contentDiv.innerHTML = formattedMessage;
            
            const timeDiv = document.createElement('div');
            timeDiv.className = 'message-time';
            const now = new Date();
            timeDiv.textContent = now.toLocaleTimeString('zh-CN', { 
                hour: '2-digit', 
                minute: '2-digit'
            });
            
            messageDiv.appendChild(contentDiv);
            messageDiv.appendChild(timeDiv);
            chatBox.appendChild(messageDiv);
            
            // 滚动到底部
            chatBox.scrollTop = chatBox.scrollHeight;
            
            // 如果是服务器消息，返回内容div以支持追加内容
            if (type === 'server') {
                return contentDiv;
            }
            return messageDiv;
        }
        
        function sendMessage() {
            const input = document.getElementById('messageInput');
            const message = input.value.trim();
            
            if (message && ws && isConnected && sessionId) {
                // 发送文本消息
                const textMessage = JSON.stringify({
                    type: "listen",
                    state: "detect",
                    text: message,
                    source: "text"  // 标记这是文本消息
                });
                ws.send(textMessage);
                // 直接显示用户消息，不等待 stt 回复
                appendMessage('user', message);
                input.value = '';
            } else if (!sessionId) {
                console.error('No session ID available');
                appendMessage('server', '连接未就绪，请稍后再试');
            }
        }
        
        function handleKeyPress(event) {
            if (event.key === 'Enter') {
                sendMessage();
            }
        }
        
        // 初始化音频上下文
        function initAudioContext() {
            if (!audioContext) {
                audioContext = new (window.AudioContext || window.webkitAudioContext)({
                    sampleRate: 16000,  // 16kHz采样率
                    latencyHint: 'interactive'
                });
                nextPlayTime = audioContext.currentTime;
                isAudioContextInitialized = true;
            }
            if (audioContext.state === 'suspended') {
                audioContext.resume();
            }
        }
        
        // 添加音频到队列
        function queueAudio(audioBlob) {
            // 确保不添加空的音频数据
            if (!audioBlob || audioBlob.size === 0) {
                console.warn('Skipping empty audio blob');
                return;
            }
            
            // 记录添加到队列的时间戳，用于调试
            const audioItem = {
                blob: audioBlob,
                timestamp: Date.now()
            };
            
            audioQueue.push(audioItem);
            console.log('Audio queued, queue length:', audioQueue.length);
            
            // 立即开始播放，不再等待缓冲区积累到特定阈值
            // 这样可以更快响应，但不会引起重复播放
            if (!isPlaying && !isPlaybackScheduled) {
                isPlaybackScheduled = true;
                setTimeout(() => {
                    isPlaying = true;
                    isPlaybackScheduled = false;
                    playNextAudio();
                }, 10);
            }
        }

        // 播放音频队列
        async function playNextAudio() {
            // 防止重复调用
            if (isPlaying && audioQueue.length === 0) {
                isPlaying = false;
                updateAIWaveAnimation(0); // 没有音频时停止动画
                return;
            }
            
            // 确保当前没有音频正在播放
            if (isPlaying && audioContext && audioContext.currentTime < nextPlayTime) {
                // 已经有音频在安排中，等待一段时间后再检查
                if (!isPlaybackScheduled) {
                    isPlaybackScheduled = true;
                    setTimeout(() => {
                        isPlaybackScheduled = false;
                        playNextAudio();
                    }, 50);
                }
                return;
            }
            
            try {
                initAudioContext();
                // 获取队列中的第一个音频项
                const audioItem = audioQueue.shift();
                if (!audioItem) {
                    isPlaying = false;
                    updateAIWaveAnimation(0);
                    return;
                }
                
                const audioBlob = audioItem.blob;
                const arrayBuffer = await audioBlob.arrayBuffer();
                
                // 检查WAV头
                const view = new DataView(arrayBuffer);
                const magic = String.fromCharCode(...new Uint8Array(arrayBuffer.slice(0, 4)));
                if (magic !== 'RIFF') {
                    throw new Error('Invalid WAV format');
                }
                
                const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
                const source = audioContext.createBufferSource();
                source.buffer = audioBuffer;

                // 创建分析器节点
                const analyser = audioContext.createAnalyser();
                analyser.fftSize = 256;
                const bufferLength = analyser.frequencyBinCount;
                const dataArray = new Float32Array(bufferLength);

                // 创建增益节点
                const gainNode = audioContext.createGain();
                source.connect(analyser);
                analyser.connect(gainNode);
                gainNode.connect(audioContext.destination);
                
                // 设置音频分析定时器
                if (animationCheckInterval) {
                    clearInterval(animationCheckInterval);
                }
                
                animationCheckInterval = setInterval(() => {
                    analyser.getFloatTimeDomainData(dataArray);
                    const audioLevel = detectAudioLevel(dataArray);
                    updateAIWaveAnimation(audioLevel);
                }, 50);
                
                // 确保音频片段之间无间隙
                const currentTime = audioContext.currentTime;
                const startTime = Math.max(currentTime, nextPlayTime);
                
                // 添加淡入淡出效果
                const fadeDuration = 0.005;
                gainNode.gain.setValueAtTime(0, startTime);
                gainNode.gain.linearRampToValueAtTime(1, startTime + fadeDuration);
                gainNode.gain.setValueAtTime(1, startTime + audioBuffer.duration - fadeDuration);
                gainNode.gain.linearRampToValueAtTime(0, startTime + audioBuffer.duration);
                
                source.start(startTime);
                nextPlayTime = startTime + audioBuffer.duration + 0.05;
                
                // 确保onended回调只执行一次
                let onendedExecuted = false;
                
                source.onended = () => {
                    if (onendedExecuted) return; // 防止多次执行
                    onendedExecuted = true;
                    
                    // 清理资源
                    try {
                        source.disconnect();
                        gainNode.disconnect();
                        analyser.disconnect();
                    } catch (e) {
                        console.warn('Error during audio node disconnection:', e);
                    }
                    
                    if (animationCheckInterval) {
                        clearInterval(animationCheckInterval);
                        animationCheckInterval = null;
                    }
                    
                    // 准备播放下一个音频
                    if (audioQueue.length > 0) {
                        // 设置一个小延迟，确保状态完全更新
                        setTimeout(() => {
                            playNextAudio();
                        }, 10);
                    } else {
                        isPlaying = false;
                        updateAIWaveAnimation(0);
                    }
                };
            } catch (e) {
                console.error('Audio playback error:', e);
                isPlaying = false;
                updateAIWaveAnimation(0);
                if (audioQueue.length > 0) {
                    setTimeout(() => playNextAudio(), 100);
                }
            }
        }

        // 页面关闭时清理连接
        window.onbeforeunload = function() {
            if (ws) {
                ws.close();
            }
        };

        // 页面加载时连接
        window.onload = () => {
            connect();
            initRecordButton();
        };

        // 添加设置面板相关功能
        function toggleSettings() {
            const panel = document.getElementById('settingsPanel');
            const overlay = document.getElementById('overlay');
            panel.classList.toggle('active');
            overlay.classList.toggle('active');
        }

        function saveSettings() {
            const newRemoteUrl = document.getElementById('remoteServerUrl').value.trim();
            const newLocalProxyUrl = document.getElementById('localProxyUrl').value.trim();
            const enableToken = document.getElementById('enableToken').checked;
            const token = document.getElementById('serverToken').value.trim();
            
            if (newRemoteUrl && newLocalProxyUrl) {
                fetch('/save_config', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({
                        ws_url: newRemoteUrl,
                        local_proxy_url: newLocalProxyUrl,
                        enable_token: enableToken,
                        token: enableToken ? token : ''
                    })
                })
                .then(response => response.json())
                .then(data => {
                    if (data.success) {
                        alert(data.message || '配置已保存');
                        if (ws) {
                            ws.close();
                            setTimeout(connect, 1000);
                        }
                    } else {
                        alert('保存配置失败：' + data.error);
                    }
                })
                .catch(error => {
                    console.error('Error saving config:', error);
                    alert('保存配置失败，请检查网络连接');
                });
            }
            toggleSettings();
        }

        // 添加 Token 开关事件监听
        document.getElementById('enableToken').addEventListener('change', function(e) {
            const tokenInput = document.getElementById('serverToken');
            const statusText = document.getElementById('tokenStatus');
            tokenInput.disabled = !e.target.checked;
            
            if (!e.target.checked) {
                tokenInput.style.backgroundColor = '#f3f4f6';
                tokenInput.style.borderColor = '#e5e7eb';
                statusText.textContent = '已关闭';
                statusText.style.color = '#374151';
            } else {
                tokenInput.style.backgroundColor = '';
                tokenInput.style.borderColor = '';
                statusText.textContent = '已开启';
                statusText.style.color = '#34c759';
            }
        });

        // 修改录音按钮样式
        function updateRecordButtonStyle(isRecording) {
            const button = document.getElementById('recordButton');
            if (isRecording) {
                button.style.background = '#ea4335';
            } else {
                button.style.background = '#34a853';
            }
        }

        // 添加音频波形动画控制
        function showAIWaveAnimation(show) {
            const wave = document.querySelector('.voice-wave');
            if (show) {
                wave.classList.add('active');
            } else {
                wave.classList.remove('active');
            }
        }

        function showUserWaveAnimation(show) {
            const wave = document.querySelector('.voice-wave');
            if (show) {
                wave.classList.add('active');
            } else {
                wave.classList.remove('active');
            }
        }

        // 添加音频电平检测
        function detectAudioLevel(audioData) {
            if (!audioData || !audioData.length) return 0;
            let sum = 0;
            for (let i = 0; i < audioData.length; i++) {
                sum += Math.abs(audioData[i]);
            }
            return sum / audioData.length;
        }

        // 更新用户说话时的波形显示
        function updateUserWaveAnimation(audioLevel) {
            const threshold = 0.01;
            const maxHeight = 24; // 最大高度
            const minHeight = 2;  // 最小高度
            
            if (audioLevel > threshold) {
                const height = Math.min(maxHeight, minHeight + (audioLevel * 100));
                document.documentElement.style.setProperty('--wave-height', `${height}px`);
                showUserWaveAnimation(true);
            } else {
                showUserWaveAnimation(false);
            }
        }

        // 更新AI说话时的头像动画
        function updateAIWaveAnimation(audioLevel) {
            const threshold = 0.01;
            const avatar = document.querySelector('.voice-avatar');
            const maxScale = 1.05;
            
            if (audioLevel > threshold) {
                // 根据音频电平计算缩放值
                const scale = 1 + (Math.min(audioLevel * 2, 0.05)); // 最大缩放到1.05
                avatar.style.transform = `scale(${scale})`;
                avatar.classList.add('speaking');
            } else {
                avatar.style.transform = 'scale(1)';
                // 注意这里不移除speaking类，让涟漪动画继续
                // 只有在完全停止说话时才移除
                if (audioLevel === 0) {
                    avatar.classList.remove('speaking');
                }
            }
        }
    </script>
</body>
</html>